package com.mm.serivce.book.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mm.base.constant.BookConstants;
import com.mm.base.exception.MuMuException;
import com.mm.domain.book.DBookInfo;
import com.mm.domain.book.DBookSource;
import com.mm.mapper.book.DBookInfoMapper;
import com.mm.mapper.book.DBookSourceMapper;
import com.mm.serivce.book.BookInfoService;
import com.mm.util.common.BookUtil;
import com.mm.util.common.FileUploadUtil;
import com.mm.util.result.ResultCode;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomUtils;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.sql.Dataset;
import org.apache.spark.sql.SparkSession;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @Description
 * @Date 2023/10/8 下午4:56
 * @Author yanglin
 **/
@Slf4j
@Service
public class BookInfoServiceImpl extends ServiceImpl<DBookInfoMapper, DBookInfo> implements BookInfoService {

    private final DBookInfoMapper bookInfoMapper;
    private final DBookSourceMapper bookSourceMapper;
    private final SparkSession sparkSession;
    private final JavaSparkContext javaSparkContext;


    public BookInfoServiceImpl(DBookInfoMapper bookInfoMapper, DBookSourceMapper bookSourceMapper, SparkSession sparkSession, JavaSparkContext javaSparkContext) {
        this.bookInfoMapper = bookInfoMapper;
        this.bookSourceMapper = bookSourceMapper;
        this.sparkSession = sparkSession;
        this.javaSparkContext = javaSparkContext;
    }

    @Override
    public Page<DBookInfo> queryBookInfoPage(Integer page, Integer limit, Integer bookWeb, Integer bookCategory, String bookTxt) {
        Page<DBookInfo> bookInfoPage = new Page<>(page, limit);
        LambdaQueryWrapper<DBookInfo> queryWrapper = new LambdaQueryWrapper<>();
        if (Objects.nonNull(bookWeb)) {
            queryWrapper.eq(DBookInfo::getBookWeb, bookWeb);
        }
        if (Objects.nonNull(bookCategory)) {
            queryWrapper.eq(DBookInfo::getBookCategory, bookCategory);
        }
        if (StrUtil.isNotBlank(bookTxt)) {
            queryWrapper.like(DBookInfo::getBookTitle, bookTxt);
        }
        queryWrapper.orderByDesc(DBookInfo::getLastUpdateTime);
        bookInfoPage = bookInfoMapper.selectPage(bookInfoPage, queryWrapper);
        return bookInfoPage;
    }

    @Override
    public DBookInfo queryBookInfoOne(Long bookId) {
        DBookInfo bookInfo = new DBookInfo();
        if (bookId > 0) {
            bookInfo = bookInfoMapper.selectById(bookId);
            DBookSource bookSource = bookSourceMapper.selectById(bookInfo.getSourceId());
            // https://www.ishuquge.la/category/1_1.html 解析文章明细
            if (BookConstants.BOOK_I_SHU_QU_GE.getCode().equals(bookSource.getSourceWeb())) {
                bookInfo = analyzeShuQuGeBookChapter(bookInfo);
            }
        }
        return bookInfo;
    }

    @Override
    public boolean removeBookInfo(Long bookId) {
        boolean flag = false;
        DBookInfo bookInfo = bookInfoMapper.selectById(bookId);
        if (Objects.nonNull(bookInfo)) {
            int result = bookInfoMapper.deleteById(bookInfo.getBookId());
            if (result > 0) {
                flag = true;
            }
        }
        return flag;
    }

    @Override
    public void syncAllBook() {
        List<DBookInfo> bookInfoNewList = new ArrayList<>();
        LambdaQueryWrapper<DBookInfo> queryWrapper = new LambdaQueryWrapper<>();
        List<DBookInfo> bookInfoList = bookInfoMapper.selectList(queryWrapper);
        if (CollectionUtil.isEmpty(bookInfoList)) {
            return;
        }
        DBookSource bookSource;
        String webMd5;
        for (DBookInfo bookInfo : bookInfoList) {
            bookSource = bookSourceMapper.selectById(bookInfo.getSourceId());
            webMd5 = DigestUtil.md5Hex(bookSource.getSourceUrl());
            // http://www.mianhuatang.tw/mulu/2-1.html
            if (BookConstants.BOOK_MAIN_HUA_TANG.getMsg().equals(webMd5)) {
                bookInfo = downloadMainHuaTangBookUrl(bookInfo);
            }
            // http://www.bookben.org/class/xuanhuan/1/
            else if (BookConstants.BOOK_SHU_BEN.getMsg().equals(webMd5)) {
                bookInfo = downloadShuBenBookUrl(bookInfo);
            }
            // http://www.janpn.net/html/woshixiongshizaitaiwenjianle.html
            else if (BookConstants.BOOK_JAB_PN.getMsg().equals(webMd5)) {
                bookInfo = downloadJanPnBookUrl(bookInfo);
            }
            // https://www.qisu12.com/txt/15211.html
            else if (BookConstants.BOOK_QI_SU_12.getMsg().equals(webMd5)) {
                bookInfo = downloadQiSu12BookUrl(bookInfo);
            }
            bookInfoNewList.add(bookInfo);
        }
        saveBatch(bookInfoNewList);
        log.info("downloadAllBook end size :{}", bookInfoList.size());
    }

    @Override
    public void syncBook(Long bookId) {
        DBookInfo bookInfo = bookInfoMapper.selectById(bookId);
        if (Objects.isNull(bookInfo)) {
            return;
        }
        log.info("downloadBook start path : {}", bookInfo.getBookUrl());
        DBookSource bookSource = bookSourceMapper.selectById(bookInfo.getSourceId());
        String webMd5 = DigestUtil.md5Hex(bookSource.getSourceUrl());
        // http://www.mianhuatang.tw/mulu/2-1.html
        if (BookConstants.BOOK_MAIN_HUA_TANG.getMsg().equals(webMd5)) {
            bookInfo = downloadMainHuaTangBookUrl(bookInfo);
        }
        // http://www.bookben.org/class/xuanhuan/1/
        else if (BookConstants.BOOK_SHU_BEN.getMsg().equals(webMd5)) {
            bookInfo = downloadShuBenBookUrl(bookInfo);
        }
        // http://www.janpn.net/html/woshixiongshizaitaiwenjianle.html
        else if (BookConstants.BOOK_JAB_PN.getMsg().equals(webMd5)) {
            bookInfo = downloadJanPnBookUrl(bookInfo);
        }
        // https://www.qisu12.com/txt/15211.html
        else if (BookConstants.BOOK_QI_SU_12.getMsg().equals(webMd5)) {
            bookInfo = downloadQiSu12BookUrl(bookInfo);
        }
        saveOrUpdate(bookInfo);
        log.info("downloadBook end path : {}", bookInfo.getBookLocalPath());
    }

    @Override
    public void syncDownloadBook(HttpServletResponse response, Long bookId) {
        DBookInfo bookInfo = bookInfoMapper.selectById(bookId);
        if (Objects.isNull(bookInfo)) {
            return;
        }
        log.info("syncDownloadBook start path : {}", bookInfo.getBookUrl());
        DBookSource bookSource = bookSourceMapper.selectById(bookInfo.getSourceId());
        String webMd5 = DigestUtil.md5Hex(bookSource.getSourceUrl());
        if (StrUtil.isBlank(bookInfo.getBookDownloadUrl())) {
            // http://www.mianhuatang.tw/mulu/2-1.html
            if (BookConstants.BOOK_MAIN_HUA_TANG.getMsg().equals(webMd5)) {
                bookInfo = getMainHuaTangDownloadPath(bookInfo);
            }
            // http://www.bookben.org/class/xuanhuan/1/
            else if (BookConstants.BOOK_SHU_BEN.getMsg().equals(webMd5)) {
                bookInfo = getShuBenBookDownloadPath(bookInfo);
            }
            // http://www.janpn.net/html/woshixiongshizaitaiwenjianle.html
            else if (BookConstants.BOOK_JAB_PN.getMsg().equals(webMd5)) {
                bookInfo = getJanPnBookDownloadPath(bookInfo);
            }
            // https://www.qisu12.com/txt/15211.html
            else if (BookConstants.BOOK_QI_SU_12.getMsg().equals(webMd5)) {
                bookInfo = getQiSu12BookDownloadPath(bookInfo);
            }
        }
        String downloadPath = bookInfo.getBookDownloadUrl();
        log.info("syncDownloadBook end path : {}", downloadPath);
        FileUploadUtil.downloadNetworkFileToBrowser(response, downloadPath, bookInfo.getBookTitle(), ".txt");

    }

    @Override
    public void downloadBook(HttpServletResponse response, Long bookId) {
        DBookInfo bookInfo = bookInfoMapper.selectById(bookId);
        if (Objects.isNull(bookInfo)) {
            return;
        }
        if (StrUtil.isBlank(bookInfo.getBookLocalPath())) {
            throw new MuMuException(ResultCode.FILE_NOT_DOWNLOAD);
        }
        FileUploadUtil.downloadLocalFileToBrowser(response, bookInfo.getBookLocalPath(), true);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void removeNotUpdateBook() {
        LambdaQueryWrapper<DBookInfo> queryWrapper = new LambdaQueryWrapper<>();
        Date now = DateUtil.offsetDay(DateUtil.date(), -1);
        queryWrapper.lt(DBookInfo::getLastUpdateTime, now);
        List<DBookInfo> bookSourceList = bookInfoMapper.selectList(queryWrapper);
        if (CollectionUtil.isNotEmpty(bookSourceList)) {
            List<Long> bookIds = bookSourceList.stream().map(b -> b.getBookId()).collect(Collectors.toList());
            bookInfoMapper.deleteBatchIds(bookIds);
            log.info("removeNotUpdateBook size {}", bookIds.size());
        }
    }

    /**
     * 下载棉花糖小说到本地
     *
     * @param bookInfo
     * @return
     */
    private DBookInfo downloadMainHuaTangBookUrl(DBookInfo bookInfo) {
        String finalFileName;
        String downloadPath;
        if (StrUtil.isBlank(bookInfo.getBookDownloadUrl())) {
            bookInfo = getMainHuaTangDownloadPath(bookInfo);
        }
        downloadPath = bookInfo.getBookDownloadUrl();
        finalFileName = FileUploadUtil.downloadNetworkFileToLocal(downloadPath, bookInfo.getBookTitle(), ".txt");
        bookInfo.setBookDownloadUrl(downloadPath);
        if (StrUtil.isNotBlank(finalFileName)) {
            bookInfo.setBookLocalPath(finalFileName);
        }
        return bookInfo;
    }

    /**
     * 棉花糖小说下载路径
     *
     * @param bookInfo
     * @return
     */
    private DBookInfo getMainHuaTangDownloadPath(DBookInfo bookInfo) {
        String downloadPath;
        String downloadPrefix = bookInfo.getBookUrl().substring(0, bookInfo.getBookUrl().lastIndexOf("/book"));
        // 页面内容 http://www.mianhuatang.tw/book/155236.html
        Document document = BookUtil.getBookInfoDocument(bookInfo.getBookUrl(), downloadPrefix);
        // 小说下载地址
        Elements elements = document.getElementsByClass("r_tools");
        if (Objects.isNull(elements)) {
            return null;
        }
        Element element = elements.get(0).children().get(3);
        // /down/txt155236.html
        String downloadUrl = element.getElementsByTag("a").attr("href");
        if (StrUtil.isBlank(downloadUrl)) {
            return null;
        }
        Document downloadDocument = BookUtil.getDocumentByUrl(downloadPrefix + downloadUrl);
        if (Objects.isNull(downloadDocument)) {
            return null;
        }
        Elements downloadTxtElements = downloadDocument.getElementsByClass("txt_down");
        Elements downloadTxtElementsVar1 = downloadTxtElements.get(0).children();
        Element downloadTxtElementVar2 = downloadTxtElementsVar1.get(1);
        String pathData = downloadTxtElementVar2.childNode(0).attr("data");
        downloadPath = pathData.split(",")[1].replaceAll("\"", "");
        bookInfo.setBookDownloadUrl(downloadPath);
        return bookInfo;
    }

    /**
     * 下载书本网小说
     *
     * @param bookInfo
     * @return
     */
    private DBookInfo downloadShuBenBookUrl(DBookInfo bookInfo) {
        String finalFileName;
        String downloadPath;
        if (StrUtil.isBlank(bookInfo.getBookDownloadUrl())) {
            bookInfo = getShuBenBookDownloadPath(bookInfo);
        }
        downloadPath = bookInfo.getBookDownloadUrl();
        finalFileName = FileUploadUtil.downloadNetworkFileToLocal(downloadPath, bookInfo.getBookTitle(), "");
        bookInfo.setBookDownloadUrl(downloadPath);
        if (StrUtil.isNotBlank(finalFileName)) {
            bookInfo.setBookLocalPath(finalFileName);
        }
        return bookInfo;
    }

    /**
     * 书本网小说下载路径
     *
     * @param bookInfo
     * @return
     */
    private DBookInfo getShuBenBookDownloadPath(DBookInfo bookInfo) {
        String downloadPath;
        String downloadPrefix = bookInfo.getBookUrl().substring(0, bookInfo.getBookUrl().lastIndexOf("/txt"));
        // 页面内容 http://www.bookben.org/txt/112691.html
        Document document = BookUtil.getBookInfoDocument(bookInfo.getBookUrl(), downloadPrefix);
        // 小说下载地址
        Elements elements = document.getElementsByClass("xz2ul");
        if (Objects.isNull(elements)) {
            return null;
        }
        List<Element> bookRElement = elements.get(0).children().stream().distinct().collect(Collectors.toList());
        Element element = bookRElement.get(1);
        // /api/txt_down.php?articleid=112691&articlename=我在异世界开报社
        downloadPath = downloadPrefix + element.getElementsByTag("a").attr("href");
        bookInfo.setBookDownloadUrl(downloadPath);
        return bookInfo;
    }

    /**
     * 下载键盘网小说
     *
     * @param bookInfo
     * @return
     */
    private DBookInfo downloadJanPnBookUrl(DBookInfo bookInfo) {
        String finalFileName;
        String downloadPath = null;
        if (StrUtil.isBlank(bookInfo.getBookDownloadUrl())) {
            bookInfo = getJanPnBookDownloadPath(bookInfo);
        }
        downloadPath = bookInfo.getBookDownloadUrl();
        finalFileName = FileUploadUtil.downloadNetworkFileToLocal(downloadPath, bookInfo.getBookTitle(), ".txt");
        bookInfo.setBookDownloadUrl(downloadPath);
        if (StrUtil.isNotBlank(finalFileName)) {
            if (StrUtil.isNotBlank(finalFileName)) {
                bookInfo.setBookLocalPath(finalFileName);
            }
        }
        return bookInfo;
    }

    /**
     * 键盘网小说下载路径
     *
     * @param bookInfo
     * @return
     */
    private DBookInfo getJanPnBookDownloadPath(DBookInfo bookInfo) {
        String downloadPath = null;
        String downloadPrefix = bookInfo.getBookUrl().substring(0, bookInfo.getBookUrl().lastIndexOf("/html"));
        // 页面内容
        // http://www.janpn.net/html/woshixiongshizaitaiwenjianle.html
        Document document = BookUtil.getBookInfoDocument(bookInfo.getBookUrl(), downloadPrefix);
        // 小说下载地址
        Elements elements = document.getElementsByClass("btn btn-lg btn-default btn-block");
        if (Objects.isNull(elements)) {
            return null;
        }
        // window.open('http://txt.janpn.net/to/txt/260/260878.txt','_self')
        String downloadStr = elements.get(0).attr("onClick");
        if (StrUtil.isNotBlank(downloadStr)) {
            downloadPath = downloadStr.split("'")[1];
        }
        bookInfo.setBookDownloadUrl(downloadPath);
        return bookInfo;
    }

    /**
     * 下载奇书网12小说
     *
     * @param bookInfo
     * @return
     */
    private DBookInfo downloadQiSu12BookUrl(DBookInfo bookInfo) {
        String finalFileName;
        String downloadPath = null;
        if (StrUtil.isBlank(bookInfo.getBookDownloadUrl())) {
            bookInfo = getQiSu12BookDownloadPath(bookInfo);
        }
        downloadPath = bookInfo.getBookDownloadUrl();
        finalFileName = FileUploadUtil.downloadNetworkFileToLocal(downloadPath, bookInfo.getBookTitle(), ".txt");
        bookInfo.setBookDownloadUrl(downloadPath);
        if (StrUtil.isNotBlank(finalFileName)) {
            if (StrUtil.isNotBlank(finalFileName)) {
                bookInfo.setBookLocalPath(finalFileName);
            }
        }
        return bookInfo;
    }

    /**
     * 奇书网12小说下载路径
     *
     * @param bookInfo
     * @return
     */
    private DBookInfo getQiSu12BookDownloadPath(DBookInfo bookInfo) {
        String downloadPath = null;
        String downloadPrefix = bookInfo.getBookUrl().substring(0, bookInfo.getBookUrl().lastIndexOf("/txt"));
        // 页面内容
        // https://www.qisu12.com/txt/15211.html
        Document document = BookUtil.getBookInfoDocument(bookInfo.getBookUrl(), downloadPrefix);
        // 小说下载地址
        Elements elements = document.getElementsByClass("l_btn_0");
        if (Objects.isNull(elements)) {
            return null;
        }
        // /api/txt_down.php?articleid=15211&articlename=返虚
        String downloadStr = elements.get(0).attr("href");
        if (StrUtil.isNotBlank(downloadStr)) {
            downloadPath = downloadPrefix + downloadStr;
        }
        bookInfo.setBookDownloadUrl(downloadPath);
        return bookInfo;
    }

    /**
     * 书趣阁-解析章节下载
     *
     * @param title
     * @param bookUrl
     * @return
     */
    private String downloadBookChapter(String title, String bookUrl) {
        Document documentCatalogue = BookUtil.getDocumentByUrl(bookUrl);
        Elements elements = documentCatalogue.select("dd>a");
        if (elements.size() == 0) {
            return "";
        }
        StringBuilder bookContent = new StringBuilder();
        for (int i = 0; i < elements.size(); i++) {
            if (i < 12) {
                continue;
            }
            // 章节内容Url
            String url = elements.get(i).attr("abs:href");
            long time = RandomUtils.nextInt(200, 300);
            log.info("downloadBook{}章, 休眠{}毫秒", i + 1, time);
            Document documentChapter = BookUtil.getDocumentByUrl(url);
            if (documentChapter.children().size() == 0) {
                continue;
            }
            // 章节标题
            String chapterTitle = documentChapter.select("h1").text();
            // 章节内容
            String chapterContent = documentChapter.getElementById("content").text();
            if (StrUtil.isNotBlank(chapterContent)) {
                chapterContent = chapterContent.replaceAll(" ", "\n")
                        .replaceAll("    ", "");
            }
            bookContent.append(chapterTitle).append("\n").append(chapterContent).append("\n");
            if (i == 20) {
                break;
            }
        }
        // 一次性写入
        String bookLocalPath = FileUploadUtil.writeBookFile(title, bookContent.toString());
        return bookLocalPath;
    }

    /**
     * 解析书趣阁小说章节
     *
     * @param bookInfo
     * @return
     */
    private DBookInfo analyzeShuQuGeBookChapter(DBookInfo bookInfo) {
        // 进入小说明细页面解析数据
        Document document = BookUtil.getDocumentByUrl(bookInfo.getBookUrl());
        Elements elementInfos = document.getElementsByClass("small");
        Elements elementIntros = document.getElementsByClass("intro");
        Elements elementTitles = document.getElementsByClass("cover");
        if (elementInfos.size() == 0 || elementIntros.size() == 0 || elementTitles.size() == 0) {
            return null;
        }
        if (elementInfos.size() > 0) {
            List<Element> infoList = elementInfos.get(0).children().stream().distinct().collect(Collectors.toList());
            if (infoList.size() > 5) {
                String bookAuthor = infoList.get(0).text();
                String bookState = infoList.get(2).text();
                String bookSize = infoList.get(3).text();
                String lastUpdateChapter = infoList.get(5).text();
                String lastUpdateChapterUrl = infoList.get(5).getElementsByTag("a").attr("href");
                bookInfo.setBookState(bookState);
                // 字数：3566875
                String bookWordNum = bookSize.substring(bookSize.indexOf("："));
                bookInfo.setBookSize(Integer.parseInt(bookWordNum));
                bookInfo.setLastUpdateChapter(lastUpdateChapter);
                bookInfo.setLastUpdateChapterUrl(lastUpdateChapterUrl);
                bookInfo.setBookAuthor(bookAuthor);
            }
        }

        if (elementIntros.size() > 0) {
            String bookIntro = elementIntros.get(0).text();
            bookInfo.setBookIntro(bookIntro);
        }

        if (elementTitles.size() > 0) {
            String bookImg = elementTitles.get(0).getElementsByTag("img").attr("src");
            bookInfo.setBookImg(bookImg);
        }
        return bookInfo;
    }

    /**
     * 使用Apache Spark进行数据去重
     * VM options中输入 -Dspark.master=local
     *
     * @param fileName
     */
    public void checkBookContentSpark(String fileName) {
        try {
            String filePath = FileUploadUtil.getFileLocalUrl(fileName);
            Dataset<String> dataset = sparkSession.read().textFile(filePath);
            // dropDuplicates去重原则：按数据行的顺序保留每行数据出现的第一条
            Dataset<String> deduplicatedData = dataset.dropDuplicates();
            String fileNewName = filePath.substring(0, filePath.indexOf(".txt")) + "-" + RandomUtil.randomString(2);
            log.info("checkBookContentSpark fileNewName : {}", fileNewName);
            deduplicatedData.write().text(fileNewName);
        } catch (Exception e) {
            log.error("checkBookContentSpark error : {}", e.getMessage());
        }
    }

}
